home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 1995…tember: Reference Library / Dev.CD Sep 95 RL / Dev.CD Sep 95 RL.toast / mac / Technical Documentation / develop / develop Issue 6 code / TCP / NewsWatcher / NW Source / Shared Code / Reusable Source / fileutil.c < prev    next >
Encoding:
C/C++ Source or Header  |  1995-07-11  |  26.0 KB  |  1,003 lines  |  [TEXT/MMCC]

  1. /*----------------------------------------------------------------------------
  2.  
  3.     fileutil.c
  4.  
  5.     This reusable module contains miscellaneous file management utility routines.
  6.     
  7.     Copyright © 1994-1995, Northwestern University.
  8.  
  9. ----------------------------------------------------------------------------*/
  10.  
  11. #include <stdio.h>
  12. #include <string.h>
  13.  
  14. #include "def.h"
  15. #include "fileutil.h"
  16. #include "strutil.h"
  17. #include "memutil.h"
  18.  
  19.  
  20.  
  21. /*----------------------------------------------------------------------------
  22.     IsEqualFSSpec 
  23.     
  24.     Compare two canonical FSSpec records.
  25.             
  26.     Entry:    file1 = pointer to first FSSpec record.
  27.             file2 = pointer to second FSSpec record.
  28.     
  29.     Exit:    function result = true if the FSSpec records are equal.
  30. ----------------------------------------------------------------------------*/
  31.  
  32. Boolean IsEqualFSSpec (FSSpec *file1, FSSpec *file2)
  33. {
  34.     return
  35.         file1->vRefNum == file2->vRefNum &&
  36.         file1->parID == file2->parID &&
  37.         EqualString(file1->name, file2->name, false, true);
  38. }
  39.  
  40.  
  41.  
  42. /*----------------------------------------------------------------------------
  43.     VolNameToVRefNum 
  44.     
  45.     Get the volume reference number given a volume name.
  46.             
  47.     Entry:    name = volume name.
  48.     
  49.     Exit:    function result = error code.
  50.             *vRefNum = volume reference number.
  51. ----------------------------------------------------------------------------*/
  52.  
  53. OSErr VolNameToVRefNum (StringPtr name, short *vRefNum)
  54. {
  55.     HParamBlockRec pb;
  56.     Str31 volNameWithColon;
  57.     short len;
  58.     OSErr err = noErr;
  59.     
  60.     CopyPascalString(volNameWithColon, name);
  61.     len = *volNameWithColon;
  62.     if (volNameWithColon[len] != ':') {
  63.         len = ++(*volNameWithColon);
  64.         volNameWithColon[len] = ':';
  65.     }
  66.     pb.volumeParam.ioNamePtr = volNameWithColon;
  67.     pb.volumeParam.ioVolIndex = -1;
  68.     err = PBHGetVInfoSync(&pb);
  69.     *vRefNum = pb.volumeParam.ioVRefNum;
  70.     return err;
  71. }
  72.  
  73.  
  74.  
  75. /*----------------------------------------------------------------------------
  76.     VolNameAndCreationDateToVRefNum 
  77.     
  78.     Get the volume reference number given a volume name and creation date.
  79.             
  80.     Entry:    name = volume name.
  81.             crDate = creation date.
  82.     
  83.     Exit:    function result = error code.
  84.             *vRefNum = volume reference number.
  85. ----------------------------------------------------------------------------*/
  86.  
  87. OSErr VolNameAndCreationDateToVRefNum (StringPtr name, long crDate, short *vRefNum)
  88. {
  89.     HParamBlockRec pb;
  90.     Str31 volName, volNameWithColon;
  91.     short len, ioVolIndex;
  92.     OSErr err = noErr;
  93.  
  94.     for (ioVolIndex = 1; ; ioVolIndex++) {
  95.         pb.volumeParam.ioNamePtr = volName;
  96.         pb.volumeParam.ioVolIndex = ioVolIndex;
  97.         err = PBHGetVInfoSync(&pb);
  98.         if (err != noErr) return err;
  99.         CopyPascalString(volNameWithColon, volName);
  100.         len = *volNameWithColon;
  101.         if (volNameWithColon[len] == ':') {
  102.             (*volName)--;
  103.         } else {
  104.             len = ++(*volNameWithColon);
  105.             volNameWithColon[len] = ':';
  106.         }
  107.         if (EqualString(name, volName, false, true) || 
  108.             EqualString(name, volNameWithColon, false, true))
  109.         {
  110.             if (pb.volumeParam.ioVCrDate == crDate) {
  111.                 *vRefNum = pb.volumeParam.ioVRefNum;
  112.                 return noErr;
  113.             }
  114.         }
  115.     }
  116. }
  117.  
  118.  
  119.  
  120. /*----------------------------------------------------------------------------
  121.     CreateTemporaryFile 
  122.     
  123.     Create a temporary file.
  124.  
  125.     Entry:    prefix = 4 character prefix for file name (e.g., caller's
  126.                 creator code).
  127.             creator = file creator.
  128.             type = file type.
  129.             
  130.     Exit:    function result = error code.
  131.             *fSpec = FSSpec record for new temporary file.
  132. ----------------------------------------------------------------------------*/
  133.  
  134. OSErr CreateTemporaryFile (FSSpec *fSpec, OSType prefix, OSType creator, OSType type)
  135. {
  136.     OSErr err = noErr;
  137.     long ticks;
  138.  
  139.     err = FindFolder(kOnSystemDisk, kTemporaryFolderType, kCreateFolder,
  140.         &fSpec->vRefNum, &fSpec->parID);
  141.     if (err != noErr) return err;
  142.     ticks = TickCount();
  143.     while (true) {
  144.         sprintf((char*)fSpec->name, "%.4s-tmp-%ld", &prefix, ticks);
  145.         c2pstr((char*)fSpec->name);
  146.         err = FSpCreate(fSpec, creator, type, smSystemScript);
  147.         if (err == noErr) return noErr;
  148.         if (err != dupFNErr) return err;
  149.         ticks++;
  150.     }
  151.     return noErr;
  152. }
  153.  
  154.  
  155.  
  156. /*----------------------------------------------------------------------------
  157.     DeleteTemporaryFiles 
  158.     
  159.     Delete all temporary files.
  160.  
  161.     Entry:    prefix = 4 character prefix for file name (e.g., caller's
  162.                 creator code).
  163.             
  164.     Exit:    function result = error code.
  165. ----------------------------------------------------------------------------*/
  166.  
  167. OSErr DeleteTemporaryFiles (OSType prefix)
  168. {
  169.     CInfoPBRec pBlock;
  170.     short vRefNum;
  171.     long dirID;
  172.     OSErr err = noErr;
  173.     Str31 fName;
  174.     short i;
  175.  
  176.     err = FindFolder(kOnSystemDisk, kTemporaryFolderType, kDontCreateFolder,
  177.         &vRefNum, &dirID);
  178.     if (err != noErr) return noErr;
  179.     
  180.     i = 1;
  181.     while (true) {
  182.         pBlock.hFileInfo.ioVRefNum = vRefNum;
  183.         pBlock.hFileInfo.ioDirID = dirID;
  184.         pBlock.hFileInfo.ioNamePtr = fName;
  185.         pBlock.hFileInfo.ioFDirIndex = i;
  186.         err = PBGetCatInfoSync(&pBlock);
  187.         if (err != noErr) return noErr;
  188.         if (((pBlock.hFileInfo.ioFlAttrib >> 4) & 1) == 0) {
  189.             if (*fName >= 4 && strncmp((char*)fName+1, (char*)&prefix, 4) == 0) {
  190.                 HDelete(vRefNum, dirID, fName);
  191.             }
  192.         }
  193.         i++;
  194.     }
  195. }
  196.  
  197.  
  198.  
  199. /*----------------------------------------------------------------------------
  200.     GetSysVolume 
  201.     
  202.     Get the volume reference number of the system volume.
  203.             
  204.     Exit:    function result = error code.
  205.             *vRefNum = vol ref num of system volume.
  206. ----------------------------------------------------------------------------*/
  207.  
  208. OSErr GetSysVolume (short *vRefNum)
  209. {
  210.     long dir;
  211.     
  212.     return FindFolder(kOnSystemDisk, kSystemFolderType, false, vRefNum, &dir);
  213. }
  214.  
  215.  
  216.  
  217. /*----------------------------------------------------------------------------
  218.     GetIndVolume 
  219.     
  220.     Get a volume reference number by volume index.
  221.     
  222.     Entry:    index = volume index
  223.             
  224.     Exit:    function result = error code.
  225.             *vRefNum = vol ref num of indexed volume.
  226. ----------------------------------------------------------------------------*/
  227.  
  228. OSErr GetIndVolume (short index, short *vRefNum)
  229. {
  230.     ParamBlockRec pb;
  231.     OSErr err = noErr;
  232.     
  233.     pb.volumeParam.ioCompletion = nil;
  234.     pb.volumeParam.ioNamePtr = nil;
  235.     pb.volumeParam.ioVolIndex = index;
  236.     
  237.     err = PBGetVInfoSync(&pb);
  238.     
  239.     *vRefNum = pb.volumeParam.ioVRefNum;
  240.     return err;
  241. }
  242.  
  243.  
  244.  
  245. /*----------------------------------------------------------------------------
  246.     MakeLegalFileName
  247.  
  248.     Make a legal file name from a string.
  249.     
  250.     Entry:    str = P-format string.
  251.             
  252.     Exit:    fileName = P-format file name.
  253.     
  254.     The string is truncated to 31 characters. Any leading period is replaced
  255.     by an underscore (_). Any embedded slashes or colons or CRs are replaced by ' '.
  256.     If the string is empty, the file name "x" is returned.
  257. ----------------------------------------------------------------------------*/
  258.  
  259. void MakeLegalFileName (StringPtr str, Str31 fileName)
  260. {
  261.     short i, len;
  262.  
  263.     len = str[0];
  264.     if (len == 0) {
  265.         CopyPascalString(fileName, "\px");
  266.         return;
  267.     }
  268.     if (len > 31) len = 31;
  269.     fileName[0] = len;
  270.     BlockMoveData(str+1, fileName+1, len);
  271.     if (fileName[1] == '.') fileName[1] = '_';
  272.     for (i = 1; i <= len; i++)
  273.         if (fileName[i] == '/' || fileName[i] == ':' || fileName[i] == CR) fileName[i] = ' ';
  274. }
  275.  
  276.  
  277.  
  278. /*----------------------------------------------------------------------------
  279.     OpenDataForkWriteCreateIfMissing
  280.     
  281.     Open the data fork of a file for writing. Create the file if it is missing..
  282.     
  283.     Entry:    fSpec = pointer to file spec.
  284.             creator = creator code.
  285.             fileType = file type.
  286.             scriptTag = script code.
  287.             append = true to open for appending data, false to open for
  288.                 rewriting data.
  289.     
  290.     Exit:    function result = error code.
  291.             *refNum = file reference number.
  292.             *empty = true if file is empty, false if append was requested
  293.                 and existing file was not empty.
  294. ----------------------------------------------------------------------------*/
  295.  
  296. OSErr OpenDataForkWriteCreateIfMissing (FSSpec *fSpec, OSType creator, OSType fileType, 
  297.     ScriptCode scriptTag, Boolean append, short *refNum, Boolean *empty)
  298. {
  299.     short fRefNum = 0;
  300.     long eof;
  301.     OSErr err = noErr;
  302.  
  303.     *empty = true;
  304.     err = FSpOpenDF(fSpec, fsRdWrPerm, &fRefNum);
  305.     if (err == noErr) {
  306.         if (append) {
  307.             err = GetEOF(fRefNum, &eof);
  308.             if (err != noErr) goto exit;
  309.             if (eof != 0) {
  310.                 *empty = false;
  311.                 err = SetFPos(fRefNum, fsFromLEOF, 0);
  312.                 if (err != noErr) goto exit;
  313.             }
  314.         } else {
  315.             err = SetEOF(fRefNum, 0);
  316.             if (err != noErr) goto exit;
  317.         }
  318.     } else if (err == fnfErr) {
  319.         err = FSpCreate(fSpec, creator, fileType, scriptTag);
  320.         if (err != noErr) goto exit;
  321.         err = FSpOpenDF(fSpec, fsRdWrPerm, &fRefNum);
  322.         if (err != noErr) goto exit;
  323.     } else {
  324.         goto exit;
  325.     }
  326.     
  327.     *refNum = fRefNum;
  328.     return noErr;
  329.     
  330. exit:
  331.  
  332.     if (fRefNum != 0) MyFSClose(fRefNum, nil);
  333.     return err;
  334. }
  335.  
  336.  
  337.  
  338. /*----------------------------------------------------------------------------
  339.     ValidateSavedFolderAlias
  340.     
  341.     Validate a saved folder alias.
  342.     
  343.     Entry:    alias = handle to alias.
  344.     
  345.     Exit:    *vRefNum = volume reference number of saved folder.
  346.             *dirID = directory ID of saved folder.
  347.             *valid = true if saved folder valid.
  348. ----------------------------------------------------------------------------*/
  349.  
  350. void ValidateSavedFolderAlias (AliasHandle alias, short *vRefNum, long *dirID, 
  351.     Boolean *valid)
  352. {
  353.     OSErr err = noErr;
  354.     FSSpec fSpec;
  355.     Boolean wasChanged;
  356.     CInfoPBRec pb;    
  357.     
  358.     *valid = false;
  359.     if (alias == nil) return;
  360.     err = ResolveAlias(nil, alias, &fSpec, &wasChanged);
  361.     if (err != noErr) return;
  362.     pb.dirInfo.ioNamePtr = fSpec.name;
  363.     pb.dirInfo.ioVRefNum = fSpec.vRefNum;
  364.     pb.dirInfo.ioFDirIndex = 0;
  365.     pb.dirInfo.ioDrDirID = fSpec.parID;
  366.     err = PBGetCatInfo(&pb, false);
  367.     if (err != noErr) return;
  368.     if (((pb.dirInfo.ioFlAttrib >> 4) & 1) == 0) return;
  369.     *vRefNum = pb.dirInfo.ioVRefNum;
  370.     *dirID = pb.dirInfo.ioDrDirID;
  371.     *valid = true;
  372. }
  373.  
  374.  
  375.  
  376. /*----------------------------------------------------------------------------
  377.     SearchFolderByCreatorAndType
  378.     
  379.     Search a folder for a file by creator and type.
  380.     
  381.     Entry:    fSpec = pointer to file spec with vRefNum and parID filled in
  382.                 for the folder to be searched.
  383.             creator = creator code.
  384.             fileType = file type.
  385.             *index = starting index in folder. Pass 1 to search the entire
  386.                 folder.
  387.     
  388.     Exit:    function result = error code.
  389.                 = fnfErr if file not found.
  390.             *fSpec = file spec of located file in folder.
  391.             *index = index of located file in folder.
  392. ----------------------------------------------------------------------------*/
  393.  
  394. OSErr SearchFolderByCreatorAndType (FSSpec *fSpec, OSType creator, OSType fileType,
  395.     short *index)
  396. {
  397.     CInfoPBRec pBlock;
  398.     FInfo fndrInfo;
  399.     OSErr err = noErr;
  400.     short i;
  401.     
  402.     i = *index;
  403.     while (true) {
  404.         pBlock.hFileInfo.ioVRefNum = fSpec->vRefNum;
  405.         pBlock.hFileInfo.ioDirID = fSpec->parID;
  406.         pBlock.hFileInfo.ioNamePtr = fSpec->name;
  407.         pBlock.hFileInfo.ioFDirIndex = i;
  408.         err = PBGetCatInfoSync(&pBlock);
  409.         if (err != noErr) return fnfErr;
  410.         if (((pBlock.hFileInfo.ioFlAttrib >> 4) & 1) == 0) {
  411.             err = FSpGetFInfo(fSpec, &fndrInfo);
  412.             if (err != noErr) return err;
  413.             if (fndrInfo.fdCreator == creator && fndrInfo.fdType == fileType) {
  414.                 *index = i;
  415.                 return noErr;
  416.             }
  417.         }
  418.         i++;
  419.     }
  420. }
  421.  
  422.  
  423.  
  424. /*----------------------------------------------------------------------------
  425.     FileOrFolderExists
  426.     
  427.     Check to see if a file or folder with a given name exists in a
  428.     directory.
  429.     
  430.     Entry:    fSpec = pointer to file spec.
  431.     
  432.     Exit:    function result = error code
  433.                 = fnfErr if file or folder doesn't exist.
  434.                 = noErr if file or folder does exist.
  435.                 = something else if error.
  436. ----------------------------------------------------------------------------*/
  437.  
  438. OSErr FileOrFolderExists (FSSpec *fSpec)
  439. {
  440.     OSErr err = noErr;
  441.     CInfoPBRec pBlock;
  442.     
  443.     pBlock.hFileInfo.ioVRefNum = fSpec->vRefNum;
  444.     pBlock.hFileInfo.ioDirID = fSpec->parID;
  445.     pBlock.hFileInfo.ioNamePtr = fSpec->name;
  446.     pBlock.hFileInfo.ioFDirIndex = 0;
  447.     return PBGetCatInfoSync(&pBlock);
  448. }
  449.  
  450.  
  451.  
  452. /*----------------------------------------------------------------------------
  453.     MakeFileNameUnique
  454.     
  455.     Generate a unique file name in a directory.
  456.     
  457.     Entry:    fSpec = pointer to file spec without suffix.
  458.             suffix = pointer to suffix string, or nil if none.
  459.     
  460.     Exit:    function result = error code.
  461.             *fSpec = file spec with unique name.
  462. ----------------------------------------------------------------------------*/
  463.  
  464. OSErr MakeFileNameUnique (FSSpec *fSpec, char *suffix)
  465. {
  466.     short n, stubLen, suffixLen, digitsLen;
  467.     char fileNameStub[32];
  468.     char digitsStr[32];
  469.     char *suffixStr;
  470.     OSErr err = noErr;
  471.     
  472.     CopyPascalString((StringPtr)fileNameStub, fSpec->name);
  473.     p2cstr((StringPtr)fileNameStub);
  474.     suffixStr = suffix == nil ? "" : suffix;
  475.     stubLen = strlen(fileNameStub);
  476.     suffixLen = strlen(suffixStr);
  477.     if (stubLen + suffixLen > 31) {
  478.         stubLen = 31 - suffixLen;
  479.         fileNameStub[stubLen] = 0;
  480.     }
  481.     n = 1;
  482.     sprintf((char*)fSpec->name, "%s%s", fileNameStub, suffixStr);
  483.     c2pstr((char*)fSpec->name);
  484.     while (true) {
  485.         err = FileOrFolderExists(fSpec);
  486.         if (err == fnfErr) return noErr;
  487.         if (err != noErr) return err;
  488.         n++;
  489.         sprintf(digitsStr, ".%d", n);
  490.         digitsLen = strlen(digitsStr);
  491.         if (stubLen + digitsLen + suffixLen > 31) {
  492.             stubLen = 31 - suffixLen - digitsLen;
  493.             fileNameStub[stubLen] = 0;
  494.         }
  495.         sprintf((char*)fSpec->name, "%s%s%s", fileNameStub, digitsStr, suffixStr);
  496.         c2pstr((char*)fSpec->name);
  497.     }
  498. }
  499.  
  500.  
  501.  
  502. /*----------------------------------------------------------------------------
  503.     MyFSWriteNoCache
  504.     
  505.     Write data to a file with no caching.
  506.     
  507.     Entry:    fRefNum = file reference number.
  508.             inOutCount = pointer to number of bytes to write.
  509.             buffer = pointer to data to write.
  510.             giveTime = pointer to function to give time to other
  511.                 processes during the write, or nil if none.
  512.     
  513.     Exit:    function result = error code.
  514.             *inOutCount = number of bytes written.
  515. ----------------------------------------------------------------------------*/
  516.  
  517. OSErr MyFSWriteNoCache (short fRefNum, long *inOutCount, Ptr buffer,
  518.     OSErr (*giveTime)(Boolean))
  519. {
  520.     ParamBlockRec pBlock;
  521.     OSErr err = noErr;
  522.     
  523.     pBlock.ioParam.ioCompletion = nil;
  524.     pBlock.ioParam.ioResult = 1;
  525.     pBlock.ioParam.ioRefNum = fRefNum;
  526.     pBlock.ioParam.ioBuffer = buffer;
  527.     pBlock.ioParam.ioReqCount = *inOutCount;
  528.     pBlock.ioParam.ioPosMode = 0x20;            /* bit 5 set = no cache */
  529.     pBlock.ioParam.ioPosOffset = 0;
  530.     err = PBWriteAsync(&pBlock);
  531.     if (err != noErr) return err;
  532.     do {
  533.         if (err == noErr && giveTime != nil) err = (*giveTime)(true);
  534.     } while (pBlock.ioParam.ioResult > 0);
  535.     *inOutCount = pBlock.ioParam.ioActCount;
  536.     if (err == noErr) err = pBlock.ioParam.ioResult;
  537.     return err;
  538. }
  539.  
  540.  
  541.  
  542. /*----------------------------------------------------------------------------
  543.     GetFileVolRefNum
  544.     
  545.     Get the volume reference number of an open file.
  546.     
  547.     Entry:    fRefNum = file reference number.
  548.     
  549.     Exit:    function result = error code.
  550.             *vRefNum = volume reference number.
  551. ----------------------------------------------------------------------------*/
  552.  
  553. OSErr GetFileVolRefNum (short fRefNum, short *vRefNum)
  554. {
  555.     FCBPBRec pBlock;
  556.     OSErr err = noErr;
  557.     
  558.     pBlock.ioRefNum = fRefNum;
  559.     pBlock.ioFCBIndx = 0;
  560.     pBlock.ioNamePtr = nil;
  561.     err = PBGetFCBInfoSync(&pBlock);
  562.     if (err != noErr) return err;
  563.     *vRefNum = pBlock.ioFCBVRefNum;
  564.     return noErr;
  565. }
  566.  
  567.  
  568.  
  569. /*----------------------------------------------------------------------------
  570.     MyFSClose
  571.     
  572.     Close an open file.
  573.     
  574.     Entry:    fRefNum = file reference number.
  575.             giveTime = pointer to function to give time to other
  576.                 processes during the close, or nil if none.
  577.     
  578.     Exit:    function result = error code.
  579. ----------------------------------------------------------------------------*/
  580.  
  581. OSErr MyFSClose (short fRefNum, OSErr (*giveTime)(Boolean))
  582. {
  583.     ParamBlockRec pBlock;
  584.     OSErr err = noErr;
  585.     short vRefNum;
  586.     
  587.     err = GetFileVolRefNum(fRefNum, &vRefNum);
  588.     if (err != noErr) return err;
  589.     
  590.     pBlock.ioParam.ioCompletion = nil;
  591.     pBlock.ioParam.ioResult = 1;
  592.     pBlock.ioParam.ioRefNum = fRefNum;
  593.     err = PBCloseAsync(&pBlock);
  594.     if (err != noErr) return err;
  595.     do {
  596.         if (err == noErr && giveTime != nil) err = (*giveTime)(true);
  597.     } while (pBlock.ioParam.ioResult > 0);
  598.     if (err == noErr) err = pBlock.ioParam.ioResult;
  599.     if (err != noErr) return err;
  600.     err = FlushVol(nil, vRefNum);
  601.     return err;
  602. }
  603.  
  604.  
  605.  
  606. /*----------------------------------------------------------------------------
  607.     GetLastModDateTime
  608.     
  609.     Get the last mod date and time of a file.
  610.     
  611.     Entry:    fSpec = pointer to file spec.
  612.     
  613.     Exit:    function result = error code.
  614.             *lastModDateTime = last mod date and time.
  615. ----------------------------------------------------------------------------*/
  616.  
  617. OSErr GetLastModDateTime(FSSpec *fSpec, unsigned long *lastModDateTime)
  618. {
  619.     CInfoPBRec pBlock;
  620.     OSErr err = noErr;
  621.     
  622.     pBlock.hFileInfo.ioNamePtr = fSpec->name;
  623.     pBlock.hFileInfo.ioVRefNum = fSpec->vRefNum;
  624.     pBlock.hFileInfo.ioFDirIndex = 0;
  625.     pBlock.hFileInfo.ioDirID = fSpec->parID;
  626.     err = PBGetCatInfoSync(&pBlock);
  627.     if (err != noErr) return err;
  628.     *lastModDateTime = pBlock.hFileInfo.ioFlMdDat;
  629.     return noErr;
  630. }
  631.  
  632.  
  633.  
  634. /*----------------------------------------------------------------------------
  635.     SetLastModDateTime
  636.     
  637.     Set the last mod date and time of a file.
  638.     
  639.     Entry:    fSpec = pointer to file spec.
  640.             lastModDateTime = last mod date and time.
  641.     
  642.     Exit:    function result = error code.
  643. ----------------------------------------------------------------------------*/
  644.  
  645. OSErr SetLastModDateTime(FSSpec *fSpec, unsigned long lastModDateTime)
  646. {
  647.     CInfoPBRec pBlock;
  648.     OSErr err = noErr;
  649.     
  650.     pBlock.hFileInfo.ioNamePtr = fSpec->name;
  651.     pBlock.hFileInfo.ioVRefNum = fSpec->vRefNum;
  652.     pBlock.hFileInfo.ioFDirIndex = 0;
  653.     pBlock.hFileInfo.ioDirID = fSpec->parID;
  654.     err = PBGetCatInfoSync(&pBlock);
  655.     if (err != noErr) return err;
  656.     pBlock.hFileInfo.ioNamePtr = fSpec->name;
  657.     pBlock.hFileInfo.ioVRefNum = fSpec->vRefNum;
  658.     pBlock.hFileInfo.ioFDirIndex = 0;
  659.     pBlock.hFileInfo.ioDirID = fSpec->parID;
  660.     pBlock.hFileInfo.ioFlMdDat = lastModDateTime;
  661.     err = PBSetCatInfoSync(&pBlock);
  662.     if (err != noErr) return err;
  663.     return noErr;
  664. }
  665.  
  666.  
  667.  
  668. /*----------------------------------------------------------------------------
  669.     CopyOneFork
  670.     
  671.     Copy one fork of a file.
  672.     
  673.     Entry:    source = pointer to source file spec.
  674.             dest = pointer to destination file spec.
  675.             resourceFork = true to copy resource fork, false to copy
  676.                 data fork.
  677.     
  678.     Exit:    function result = error code.
  679. ----------------------------------------------------------------------------*/
  680.  
  681. static OSErr CopyOneFork (FSSpec *source, FSSpec *dest, Boolean resourceFork)
  682. {
  683.     short sourceRefNum = 0;
  684.     short destRefNum = 0;
  685.     long fileSize, len;
  686.     Ptr buf;
  687.     FInfo fInfo;
  688.     OSErr err = noErr;
  689.     
  690.     /* Open source fork. */
  691.     
  692.     if (resourceFork) {
  693.         err = FSpOpenRF(source, fsRdPerm, &sourceRefNum);
  694.     } else {
  695.         err = FSpOpenDF(source, fsRdPerm, &sourceRefNum);
  696.     }
  697.     if (err == fnfErr) return noErr;
  698.     if (err != noErr) goto exit;
  699.     err = GetEOF(sourceRefNum, &fileSize);
  700.     if (err != noErr) goto exit;
  701.     
  702.     /* Open destination fork. Create the fork if it is missing. */
  703.     
  704.     if (resourceFork) {
  705.         err = FSpOpenRF(dest, fsRdWrPerm, &destRefNum);
  706.     } else {
  707.         err = FSpOpenDF(dest, fsRdWrPerm, &destRefNum);
  708.     }
  709.     if (err == fnfErr) {
  710.         err = FSpGetFInfo(source, &fInfo);
  711.         if (err != noErr) goto exit;
  712.         if (resourceFork) {
  713.             FSpCreateResFile(dest, fInfo.fdCreator, fInfo.fdType, smSystemScript);
  714.             err = ResError();
  715.         } else {
  716.             err = FSpCreate(dest, fInfo.fdCreator, fInfo.fdType, smSystemScript);
  717.         }
  718.         if (err != noErr) goto exit;
  719.         if (resourceFork) {
  720.             err = FSpOpenRF(dest, fsRdWrPerm, &destRefNum);
  721.         } else {
  722.             err = FSpOpenDF(dest, fsRdWrPerm, &destRefNum);
  723.         }
  724.     }
  725.     if (err != noErr) goto exit;
  726.     err = SetFPos(destRefNum, fsFromStart, 0);
  727.     if (err != noErr) goto exit;
  728.     
  729.     /* Copy the source fork to the destination fork. */
  730.     
  731.     err = MyNewPtr(1024, &buf);
  732.     if (err != noErr) goto exit;
  733.     while (fileSize > 0) {
  734.         len = fileSize > 1024 ? 1024 : fileSize;
  735.         err = FSRead(sourceRefNum, &len, buf);
  736.         if (err != noErr) goto exit;
  737.         err = FSWrite(destRefNum, &len, buf);
  738.         if (err != noErr) goto exit;
  739.         fileSize -= len;
  740.     }
  741.     
  742. exit:
  743.  
  744.     if (sourceRefNum != 0) MyFSClose(sourceRefNum, nil);
  745.     if (destRefNum != 0) MyFSClose(destRefNum, nil);
  746.     if (buf != nil) MyDisposePtr(buf);
  747.     return err;
  748. }
  749.  
  750.  
  751.  
  752. /*----------------------------------------------------------------------------
  753.     CopyFile
  754.     
  755.     Make a copy of a file (both forks).
  756.     
  757.     Entry:    source = pointer to source file spec.
  758.             dest = pointer to destination file spec.
  759.     
  760.     Exit:    function result = error code.
  761. ----------------------------------------------------------------------------*/
  762.  
  763. OSErr CopyFile (FSSpec *source, FSSpec *dest)
  764. {
  765.     OSErr err = noErr;
  766.  
  767.     err = CopyOneFork(source, dest, true);
  768.     if (err != noErr) return err;
  769.     return CopyOneFork(source, dest, false);
  770. }
  771.  
  772.  
  773.  
  774. /*----------------------------------------------------------------------------
  775.     GetVolList
  776.     
  777.     Get the list of volumes to be scanned.
  778.             
  779.     Exit:    function result = error code.
  780.             *volList = handle to list of vol ref nums.
  781.             *numVols = number of volumes in list.
  782. ----------------------------------------------------------------------------*/
  783.  
  784. OSErr GetVolList (TVolListHandle *volList, short *numVols)
  785. {
  786.     TVolListHandle v = nil;
  787.     OSErr err = noErr;
  788.     short n = 0;
  789.     HParamBlockRec pBlock;
  790.     short volIndex = 1;
  791.     
  792.     err = MyNewHandle(0, &v);
  793.     if (err != noErr) goto exit;
  794.     
  795.     while (true) {
  796.         memset(&pBlock, 0, sizeof(pBlock));
  797.         pBlock.volumeParam.ioVolIndex = volIndex;
  798.         err = PBHGetVInfoSync(&pBlock);
  799.         if (err == nsvErr) break;
  800.         if (err != noErr) goto exit;
  801.         n++;
  802.         err = MySetHandleSize(v, n * sizeof(short));
  803.         (*v)[n-1] = pBlock.volumeParam.ioVRefNum;
  804.         volIndex++;
  805.     }
  806.  
  807.     *volList = v;
  808.     *numVols = n;
  809.     return noErr;
  810.     
  811. exit:
  812.  
  813.     MyDisposeHandle(v);
  814.     return err;
  815. }
  816.  
  817.  
  818.  
  819. /*----------------------------------------------------------------------------
  820.     GetFullPath
  821.     
  822.     Get the full path name of a file.
  823.     
  824.     Entry:    fSpec = pointer to file spec.
  825.             
  826.     Exit:    function result = error code.
  827.             *fullPath = handle to array of Str31 strings listing the 
  828.                 components of the path in reverse order (file name
  829.                 first, volume name last).
  830.             *numComponents = number of components in the path (size of
  831.                 fullPath array).
  832. ----------------------------------------------------------------------------*/
  833.  
  834. OSErr GetFullPath (FSSpec *fSpec, Str31 ***fullPath, short *numComponents)
  835. {
  836.     OSErr err = noErr;
  837.     Str31 **h = nil;
  838.     short n = 0;
  839.     CInfoPBRec pBlock;
  840.     Str31 component;
  841.     
  842.     err = MyNewHandle(0, &h);
  843.     if (err != noErr) goto exit;
  844.     
  845.     CopyPascalString(component, fSpec->name);
  846.     pBlock.dirInfo.ioNamePtr = component;
  847.     pBlock.dirInfo.ioVRefNum = fSpec->vRefNum;
  848.     pBlock.dirInfo.ioDrParID = fSpec->parID;
  849.     pBlock.dirInfo.ioFDirIndex = -1;
  850.     while (true) {
  851.         n++;
  852.         err = MySetHandleSize(h, n * sizeof(Str31));
  853.         if (err != noErr) goto exit;
  854.         CopyPascalString((*h)[n-1], component);
  855.         if (pBlock.dirInfo.ioDrParID == fsRtParID) break;
  856.         pBlock.dirInfo.ioDrDirID = pBlock.dirInfo.ioDrParID;
  857.         err = PBGetCatInfoSync(&pBlock);
  858.         if (err != noErr) goto exit;
  859.     }
  860.     
  861.     *fullPath = h;
  862.     *numComponents = n;
  863.     return noErr;
  864.     
  865. exit:
  866.  
  867.     MyDisposeHandle(h);
  868.     return err;
  869. }
  870.  
  871.  
  872.  
  873. /*----------------------------------------------------------------------------
  874.     VolSupportsPBCatSearch
  875.     
  876.     Determine whether or not a volume supports the PBCatSearch function.
  877.     
  878.     Entry:    vRefNum = vol ref num.
  879.             
  880.     Exit:    function result = error code.
  881.             *supportsPBCatSearch = true if PBCatSearch supported.
  882. ----------------------------------------------------------------------------*/
  883.  
  884. OSErr VolSupportsPBCatSearch (short vRefNum, Boolean *supportsPBCatSearch)
  885. {
  886.     OSErr err = noErr;
  887.     HParamBlockRec pBlock;
  888.     GetVolParmsInfoBuffer buf;
  889.     
  890.     pBlock.ioParam.ioNamePtr = nil;
  891.     pBlock.ioParam.ioVRefNum = vRefNum;
  892.     pBlock.ioParam.ioBuffer = (Ptr)&buf;
  893.     pBlock.ioParam.ioReqCount = sizeof(buf);
  894.     err = PBHGetVolParmsSync(&pBlock);
  895.     if (err != noErr) return err;
  896.     *supportsPBCatSearch = (buf.vMAttrib & (1L << bHasCatSearch)) != 0;
  897.     return noErr;
  898. }
  899.  
  900.  
  901.  
  902. /*----------------------------------------------------------------------------
  903.     MakeFinderAliasFile 
  904.     
  905.     Make a Finder alias file.
  906.     
  907.     Entry:    *aliasFSpec = file spec for alias file to be created.
  908.             *targetFSpec = file spec for target object.
  909.     
  910.     Exit:    function result = error code.
  911.  
  912.     Warning: Any existing file at "aliasFSpec" is deleted!
  913. ----------------------------------------------------------------------------*/
  914.  
  915. OSErr MakeFinderAliasFile (FSSpec *aliasFSpec, FSSpec *targetFSpec)
  916. {
  917.     AliasHandle alias = nil;
  918.     OSErr err = noErr;
  919.     short refNum = 0;
  920.     Boolean fileCreated = false;
  921.     FInfo fndrInfo;
  922.     
  923.     err = FSpGetFInfo(targetFSpec, &fndrInfo);
  924.     if (err != noErr) return err;
  925.     err = NewAlias(nil, targetFSpec, &alias);
  926.     if (err != noErr) return err;
  927.     FSpDelete(aliasFSpec);
  928.     FSpCreateResFile(aliasFSpec, fndrInfo.fdCreator, 'adrp', smSystemScript);
  929.     err = ResError();
  930.     if (err != noErr) goto exit;
  931.     fileCreated = true;
  932.     refNum = FSpOpenResFile(aliasFSpec, fsRdWrPerm);
  933.     err = ResError();
  934.     if (err != noErr) goto exit;
  935.     AddResource((Handle)alias, 'alis', 0, "\p");
  936.     err = ResError();
  937.     if (err != noErr) goto exit;
  938.     CloseResFile(refNum);
  939.     err = FSpGetFInfo(aliasFSpec, &fndrInfo);
  940.     if (err != noErr) goto exit;
  941.     fndrInfo.fdFlags = 0x8000;    /* set bit 15 = alias flag */
  942.     err = FSpSetFInfo(aliasFSpec, &fndrInfo);
  943.     if (err != noErr) goto exit;
  944.     return noErr;
  945.  
  946. exit:
  947.  
  948.     if (alias != nil) DisposeHandle((Handle)alias);
  949.     if (refNum != 0) CloseResFile(refNum);
  950.     if (fileCreated) FSpDelete(aliasFSpec);
  951.     return err;
  952. }
  953.  
  954.  
  955.  
  956. /*----------------------------------------------------------------------------
  957.     DeleteFolder
  958.     
  959.     Delete a folder.
  960.     
  961.     Entry:    fSpec->vRefNum = vol ref num of vol containing folder.
  962.             fSpec->parID = dirID of folder.
  963.             onlyContents = true to delete only the contents of the folder,
  964.                 false to delete the contents and the folder itself.
  965.     
  966.     Exit:    function result = error code.
  967. ----------------------------------------------------------------------------*/
  968.  
  969. OSErr DeleteFolder (FSSpec *fSpec, Boolean onlyContents)
  970. {
  971.     OSErr err = noErr;
  972.     CInfoPBRec pBlock;
  973.     FSSpec gSpec;
  974.     
  975.     while (true) {
  976.         pBlock.hFileInfo.ioNamePtr = fSpec->name;
  977.         pBlock.hFileInfo.ioVRefNum = fSpec->vRefNum;
  978.         pBlock.hFileInfo.ioDirID = fSpec->parID;
  979.         pBlock.hFileInfo.ioFDirIndex = 1;
  980.         err = PBGetCatInfoSync(&pBlock);
  981.         if (err == fnfErr) break;
  982.         if (err != noErr) return err;
  983.         if ((pBlock.hFileInfo.ioFlAttrib & ioDirMask) != 0) {
  984.             gSpec.vRefNum = fSpec->vRefNum;
  985.             gSpec.parID = pBlock.dirInfo.ioDrDirID;
  986.             err = DeleteFolder(&gSpec, true);
  987.             if (err != noErr) return err;
  988.         }
  989.         err = FSpDelete(fSpec);
  990.         if (err != noErr) return err;
  991.     }
  992.     if (onlyContents) return noErr;
  993.     pBlock.dirInfo.ioNamePtr = gSpec.name;
  994.     pBlock.dirInfo.ioVRefNum = fSpec->vRefNum;
  995.     pBlock.dirInfo.ioDrDirID = fSpec->parID;
  996.     pBlock.dirInfo.ioFDirIndex = -1;
  997.     err = PBGetCatInfoSync(&pBlock);
  998.     if (err != noErr) return err;
  999.     gSpec.vRefNum = pBlock.dirInfo.ioVRefNum;
  1000.     gSpec.parID = pBlock.dirInfo.ioDrParID;
  1001.     return FSpDelete(&gSpec);
  1002. }
  1003.